package cmd

import (
	"fmt"
	"go_gvm_mini/instructions"
	"go_gvm_mini/instructions/base"
	"go_gvm_mini/rtda"
)

func interpret(thread *rtda.JThread) {
	defer catchErr(thread)
	loop(thread)
}

func catchErr(thread *rtda.JThread) {
	if r := recover(); r != nil {
		panic(r)
	}
}

func loop(thread *rtda.JThread) {
	reader := &base.ByteCodeReader{}
	for {
		frame := thread.CurJFrame()
		pc := frame.NextPC()
		thread.SetPc(pc)

		//decode
		reader.Reset(frame.Method().Code(), pc)
		opcode := reader.ReadUInt8()
		inst := instructions.NewInstruction(opcode)
		inst.FetchOperands(reader)
		frame.SetNextPC(reader.PC())
		logInstruction(frame, inst)
		inst.Execute(frame)
		if thread.IsStackEmpty() {
			break
		}
	}
}

func logInstruction(frame *rtda.JFrame, inst base.Instruction) {
	method := frame.Method()
	className := method.Class().Name()
	methodName := method.Name()
	pc := frame.JThread().PC()
	fmt.Printf("%v.%v() #%2d %T %v\n", className, methodName, pc, inst, inst)
}
